home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / Mac⁄gnuucp 6.14 / source / gnuucp.c < prev    next >
Encoding:
Text File  |  1994-01-21  |  43.6 KB  |  1,718 lines  |  [TEXT/KAHL]

  1. /*
  2.  * @(#)gnuucp.c 1.29 87/09/29    Copyright 1987 Free Software Foundation, Inc.
  3.  *
  4.  * Copying and use of this program are controlled by the terms of the
  5.  * GNU Emacs General Public License.
  6.  *
  7.  * Derived from:
  8.  * i[$]uuslave.c    1.7 08/12/85 14:04:20
  9.  * which came from the ACGNJ BBS system at +1 201 753 9758.  Original
  10.  * author unknown.  Many people, too numerous to list here, have contributed
  11.  * changes and ports to this program.
  12.  */
  13.  
  14. /*
  15. 6. 11     -     Color Icons
  16.             57,600 baud modem support
  17.             getstring fix
  18. 6. 12   -     Support Taylor UUCP by turning of ACK's 
  19.               after a CLOSE message has arrived.
  20.               Make MAX_STRING 1024
  21. */
  22.  
  23. char version[] = "Version Fort Pond Research-6.14";
  24. #include <console.h>
  25. #include <Serial.h>
  26. #include <SegLoad.h>
  27. #include <signal.h>
  28. #include <profile.h>
  29. #include <pascal.h>
  30. #include <Memory.h>
  31. extern int inRefNum;
  32. /*
  33.  
  34. This program implements the uucp (Unix-to-Unix CoPy) protocol, used to
  35. transfer mail, files, and Usenet news among Unix machines.  UUCP comes
  36. free with Unix (unless you get a sleazeoid version like Xenix, where
  37. they charge you extra for it), but you don't get sources.  You can buy
  38. a commercial program for MSDOS, called UULINK, which also implements
  39. this protocol.  UULINK costs $300 and you still don't get sources.
  40. This program not only runs on Unix and MSDOS, but on many other
  41. machines, and comes with the Free Software Foundation copyright, which
  42. guarantees you access to sources.
  43.  
  44. The protocol requires a full 8-bit data path with no characters inserted
  45. or deleted (e.g. ^S and ^Q are used as DATA characters).  Simple serial
  46. ports and modems do this; most complicated networks do not, at least without
  47. setting up odd modes and such.  Telenet's PC Pursuit works fine though.
  48.  
  49. The basic flow of the protocol is that the calling machine will send down
  50. a line of text saying what it wants to do (send a file, receive a file,
  51. or hang up).  (The lines of text are encapsulated into packets; see below.)
  52. The called machine responds with a "yes" or "no" answer, and if the answer
  53. was yes, it sends or receives the file.  Files are terminated with a
  54. packet containing 0 bytes of data.  Then the system that received the file
  55. sends a "copy succeeded" or "copy failed" line to the other end, and 
  56. they go back to "what do we do now".  A request to hang up should be
  57. answered "no" if the called machine has some mail or files it wants to
  58. send to the calling machine; the two machines reverse roles and the calling
  59. machine goes into "what do we do now".  If a hangup request is answered "yes",
  60. the call is terminated.
  61.  
  62. The data flow described above is actually sent by a lower level "protocol
  63. module".  The default "g" protocol module sends the data in packets containing
  64. checksums and acknowledgements.  Each packet can either hold a short
  65. control message, e.g. an ack, or a data block.  The data blocks are
  66. numbered with a 3-bit counter, and sent with a checksum.  If the sender
  67. has not received an acknowledgement for a data block within a certain
  68. time, it retransmits the block.  The size of a data block is negotiated
  69. at the start of a call.  To send a block with fewer bytes, a "short
  70. data" block is sent, which is just as big as a "long data" block, but
  71. contains a 1- or 2-byte count of "how many bytes in this block are just
  72. padding".  This is a cute trick since it always works (e.g. if you want
  73. to send 1023 out of 1024 bytes, you only need one byte for the count;
  74. while if you want to send 1 byte out of 1024 then you have enough space
  75. for the count to be 2 bytes).
  76.  
  77. The short control messages are used to start the call and negotiate the
  78. packet size and the "window size", to acknowledge or reject packets,
  79. and to terminate the packet protocol at the end of a call.  The window
  80. size is how many packets one side can send before it will stop and wait
  81. for an acknowledgement from the other side.  A window size of 1 makes
  82. for a half-duplex protocol (which is what gnuucp currently
  83. implements), but also makes it easy to implement on micros that don't
  84. handle serial lines with interrupts.  In window 1, you just keep
  85. sending the same packet until the other side acknowledges it.  Unix
  86. always uses a window size of 3, which is the max that can be dealt with
  87. given the 3-bit packet numbers (for reasons that would take more space
  88. than I want to spend here).  This gives much better throughput, but
  89. requires full duplex serial port handling and more complicated
  90. acknowledgement strategies.
  91.  
  92. In receiving data, the "g" protocol scans for a DLE (hex 10) which
  93. indicates the start of a packet, and the next 5 bytes are read and
  94. checked.  If they pass, this is a good packet and it is acted upon.
  95. (If it's a data packet, we have to read in and check the data part
  96. too.)  If the checks fail, all the bytes read so far should be rescanned
  97. for another DLE, since it's possible that some characters were dropped
  98. and the first DLE we saw was actually part of a data packet.
  99.  
  100. Other protocol modules have been implemented; e.g. the "f" protocol which
  101. eliminates the packets and acknowledgements, for reduced overhead when
  102. running over an error-free, flow controlled connection on an X.25 network.
  103. There has been talk of a "z" protocol which implements ZMODEM.  So far,
  104. none of these modules are supplied with gnuucp, though "f" is public domain
  105. and "z" is intended to be, because they are not ported and tested yet.
  106.  
  107. At the start of a call, the caller sends the list of protocols it supports,
  108. and the callee picks the best one it knows and says "use this one please".
  109.  
  110. At the low level, full 8-bit bytes are sent out and received on an async
  111. serial port.
  112. */
  113.  
  114. #include "includes.h"        /* System include files, system dependent */
  115. #include "uucp.h"        /* Uucp definitions and parameters */
  116.  
  117. #define    MAX_FLAGS    40
  118.  
  119. extern int errno;
  120.  
  121. char    Progname[NAMESIZE];            /* Our program name e.g. "uuq" */
  122. char    ttynam[NAMESIZE],        /* Name of tty we use as serial port */
  123.     srcnam[NAMESIZE],        /* Source file name */
  124.     dstnam[NAMESIZE],        /* Dest file name */
  125.     flags[MAX_FLAGS],        /* Flags from file xfer cmd */
  126.     temp[NAMESIZE];            /* Temp file name */
  127.  
  128. int    fdtty,                /* Opaque handle to pass to protocol */
  129.     ignore_time_restrictions = 0,    /* Call out even if L.sys sez no */
  130.     mode;                /* File mode from file xfer cmd */
  131.  
  132. int   f_wait    = 0;    /* FIXME, unreferenced now */
  133. int   loop    = 1;    /* Loop accepting logins if tty name specified */
  134. int   curtemp = 0;
  135.  
  136. #define    MAX_STRING    1024    /* Max length string to send/expect */
  137.  
  138. /* Strings sent by the slave side to the master */
  139. char msgo0[] = "\nlogin:";
  140. char msgo1[] = "Password:";
  141. /* char msgo2[] = "\20Shere\0"; Now we send it specially */
  142. char msgo3[] = "\20ROK\0";
  143. char msgo3a[]= "\20Pg\0";
  144. char msgo4[] = "\20OOOOOOO\0";
  145.  
  146. /* Strings sent by the master to the slave */
  147. char msgi0[] = "uucp\n";
  148. char msgi1[] = "s8000\n";
  149. /* char msgi2[] = "\20S*\0"; We now scan it specially FIXME */
  150. char msgi3[] = "\20Ug\0";
  151. char msgi4[] = "OOOOOO";
  152.  
  153. /*
  154.  * Protocol switch data structure
  155.  */
  156. struct proto {
  157.     char    p_id;
  158.     int    (*p_turnon)(int);
  159.     int    (*p_rdmsg)(char *, int);
  160.     int    (*p_wrmsg)(char, char *, int);
  161.     int    (*p_rddata)(int, FILE *);
  162.     int    (*p_wrdata)(FILE *, int);
  163.     int    (*p_turnoff)(void);
  164. };
  165.  
  166. /* extern int gturnon(), grdmsg(), gwrmsg(char), grddata(), gwrdata(), gturnoff(); */
  167. /*
  168.  * This is how the "f" protocol would be declared:
  169.  * extern int fturnon(), frdmsg(), fwrmsg(), frddata(), fwrdata(), fturnoff();
  170.  */
  171.  
  172. struct proto ptbl[] = {
  173.     /* "f" protocol to run on X.25 PADs */
  174.   /*    {'f', fturnon, frdmsg, fwrmsg, frddata, fwrdata, fturnoff}, */
  175.     /* Original "g" protocol for dialup async modem lines */
  176.     {'g', gturnon, grdmsg, gwrmsg, grddata, gwrdata, gturnoff},
  177. };
  178.  
  179. struct proto *curproto = &ptbl[0];
  180.  
  181. #define    turnon    (*curproto->p_turnon)
  182. #define    rdmsg    (*curproto->p_rdmsg)
  183. #define    wrmsg    (*curproto->p_wrmsg)
  184. #define    rddata    (*curproto->p_rddata)
  185. #define    wrdata    (*curproto->p_wrdata)
  186. #define    turnoff    (*curproto->p_turnoff)
  187.  
  188. /*
  189.  * Read from the serial port a null-terminated string.
  190.  * The string FIXME should not overrun MAX_STRING.
  191.  * String starts with first printing character.
  192.  */
  193. int
  194. getstring(where)
  195.     register char *where;
  196. {
  197.     register int data, count = 0;
  198.     DEBUG(8, "Getstring:  ", 0);
  199.     /* Read data until null character */
  200.     while ( (long)((data = xgetc()) != (long)EOF)) {
  201.         if (count < MAX_STRING)
  202.             {
  203.                 data &= 0x7F;
  204.                 if (DEBUG_LEVEL(8)) 
  205.                     {
  206.                         printf("%02x%c ", data, isprint(data)? data: ' ');
  207.                         }
  208.                 where[count++] = data;
  209.                 if (count == 1 && !isprint(where[0]))
  210.                     count = 0;
  211.                 if (data == 0x00 && count != 0) 
  212.                     {
  213.                         if (DEBUG_LEVEL(8)) putchar('\n');
  214.                         return SUCCESS;
  215.                         }
  216.                 }
  217.             else
  218.               {
  219.                   where[MAX_STRING-1]= '\0';
  220.                   logit("String too long for buffer", where);
  221.                   break;
  222.                   }
  223.         }
  224.     if (DEBUG_LEVEL(8)) putchar('\n');
  225.     return FAIL;
  226. }
  227.  
  228.  
  229. /*
  230.  * Medium level input routine.
  231.  *
  232.  * Look for an input string for the send-expect sequence.
  233.  * Return 0 for matching string, 1 for timeout before we found it.
  234.  * FIXME:  we only time out if the other end stops sending.  If it
  235.  *       keeps sending, we keep listening forever.
  236.  */
  237. instr(s,n, level)
  238. register char *s;
  239. register int n;
  240. int level;
  241. {
  242.     register int data;
  243.     register char    *pattern;
  244.     register int i;
  245.  
  246.     if (DEBUG_LEVEL(2)) {
  247.         printf("Expecting ");
  248.         for (i = 0; i < n; i++)
  249.             printf("%02x%c ",s[i] & 0xFF, isprint(s[i])? s[i]: ' ');
  250.         printf("\nR ");
  251.     }
  252.  
  253.     pattern = s;
  254.  
  255.     while ((long)(data = xgetc()) != (long)EOF) {
  256.         data &= 0x7F;            /* Ignore parity */
  257.  
  258.         if (DEBUG_LEVEL(2)) {
  259.             printf("%02x%c ", data, isprint(data)? data: ' ');
  260.         }
  261.  
  262.     tryfirst:
  263.  
  264.         if (data == *pattern) {
  265.             /* Input matches next byte of pattern */
  266.             pattern++;
  267.             if (pattern >= &s[n]) {
  268.                 if (DEBUG_LEVEL(2)) putchar('\n');
  269.                 return SUCCESS;        /* Done */
  270.             }
  271.         } else {
  272.             /* Input doesn't match pattern */
  273.             if (pattern == s) continue;    /* First char */
  274.             /* FIXME!  This doesn't work if pattern can match
  275.              * in more than one way initially, e.g.:
  276.              * "NECT" works when "CONNECT" is output, but
  277.              * "NNECT" fails when "CONNNECT" is output... */
  278.             pattern = s;    /* Does cur char match first? */
  279.             goto tryfirst;    /* FIXME */
  280.         }
  281.     }
  282.  
  283.     if (DEBUG_LEVEL(2))    putchar('\n');
  284.     DEBUG(level, "Didn't see expected string '%s'\n", s);
  285.     return FAIL;
  286. }
  287.  
  288. /*
  289.  * Debugging hack for stuff written to the modem.
  290.  */
  291. int
  292. twrite(s, n)
  293.     char *s;
  294.     int    n;
  295. {
  296.     register int i;
  297.  
  298.     if (DEBUG_LEVEL(2)) {
  299.         printf("Wrote:  ");
  300.         for (i = 0; i < n; i++)
  301.             printf("%02x%c ",s[i] & 0xFF, isprint(s[i])? s[i]: ' ');
  302.         printf("\n");
  303.     }
  304.  
  305.     return xwrite(/* 0, */s, n);
  306. }
  307.  
  308. short apref;
  309. extern char *argv;
  310. char input_str[256];
  311.  
  312. extern int orig_debug;
  313. /*
  314.  * MAIN ROUTINE.
  315.  *
  316.  * This is called at program startup.  It parses the arguments to the
  317.  * program (if any) and sets up to receive a call on the modem.
  318.  *
  319.  * If there are no arguments, we assume the caller is already on standard
  320.  * input, waiting to do uucp protocols (past the login prompt), and we
  321.  * just handle one caller.
  322.  *
  323.  * If there is an argument, it is the name of the tty device where we
  324.  * should listen for multiple callers and handle login and password.
  325.  */
  326. main(argc,argv)
  327. int argc;
  328. char *argv[];
  329. {
  330.     int    ontheline = 1;
  331.     clock_t start_time;
  332.     clock_t end_time;
  333.     int    i;
  334.     int c;
  335.     char tmp_str[256];
  336.     char *poll_sys = (char *)NULL;  /* System name to poll, or none */
  337.     int c1, c2;
  338.     int retval;
  339.     FILE *input_file;
  340.     short vRefNum;
  341.     char apname[256];
  342.     short apWhatToDo;
  343.     short apCount;
  344.     AppFile appInfo;
  345.     FInfo **apparm;
  346.     SetApplLimit(GetApplLimit()-100000);
  347.     _ftype = 'TEXT';
  348.     _fcreator = 'ttxt';
  349.     MaxApplZone();
  350.     MoreMasters();
  351.     MoreMasters();
  352.     MoreMasters();
  353.     MoreMasters();
  354.     MoreMasters();
  355.     MoreMasters();
  356.     srand(time(NULL));
  357.     GetAppParms((unsigned char *)&apname, &apref, (Handle *)&apparm);
  358.     CountAppFiles(&apWhatToDo, &apCount);
  359.     if (apCount > 1)
  360.         {
  361.             printf("To many Documents selected on startup: %s\n", apname);
  362.             exit(1);
  363.             }
  364.     if (apWhatToDo != appOpen)
  365.         {
  366.             logit("Can't print a connection file: %s\n", apname);
  367.             exit(1);
  368.             }
  369.     
  370. #ifdef MSDOS
  371.     fprintf(stderr, "\n\n");
  372.     fprintf(stderr, "UUCICO  : for MS-DOS             Ver 0787-2.05a\n");
  373.     fprintf(stderr, "Base code from gnuucp - hoptoad version 1.27\n");
  374.     fprintf(stderr, "Copyright 1987 Free Software Foundation, Inc.  Copying by GNU rules only,\n");
  375.     fprintf(stderr, "e.g. copies must include source code as well as binaries!\n");
  376.     fprintf(stderr, "Modified by Garry M. Paxinos\n");
  377.     fprintf(stderr, "FidoNet : 135/6   the 'Eye of Osiris'   SEAdog/OPUS/Dutchie/uuslave\n");
  378.     fprintf(stderr, "        : 135/17  Megasystems Online    OPUS/Dutchie\n");
  379.     fprintf(stderr, "UUCP    : ...!{allegra|codas|ucf-cs}!novavax!ankh!pax\n");
  380.     fprintf(stderr, "USNail  : % Megasystems Inc. 1075 Broken Sound Pkwy NW, Boca Raton FL 33431\n\n");
  381.  
  382. #endif
  383.     if (signal(SIGINT, sigint) == SIG_ERR) {
  384.         fprintf(stderr,"Couldn't set SIGINT\n");
  385.         abort();
  386.         }
  387.     console_options.pause_atexit = true;
  388.     printf("\n");
  389.     SetWTitle((WindowPtr)(stdout->window), "\pMac/gnuucp");
  390.     GetVol(0L, &vRefNum);
  391.     if (apCount > 0)
  392.         {
  393.            GetAppFiles(1, &appInfo);
  394.            PtoCstr((unsigned char *)appInfo.fName);
  395.            SetVol(0L, appInfo.vRefNum);
  396.            input_file = fopen((char *)appInfo.fName, "r");
  397.            if (input_file == NULL)
  398.                    {
  399.                        printf("Can't open input file: %s\n", (char *)appInfo.fName);
  400.                        exit(1);
  401.                        }
  402.                if (fgets(input_str, 255, input_file) == NULL)
  403.                    {
  404.                        printf("Can't get input command line from dial file\n");
  405.                        exit(1);
  406.                        }
  407.                if (input_str[strlen(input_str)-1] == '\n')
  408.                    input_str[strlen(input_str)-1] = '\0';
  409.                PtoCstr((unsigned char *)apname);
  410.                sprintf(tmp_str, "%s %s", apname, input_str);
  411.                CtoPstr(apname);
  412.                strcpy(input_str, tmp_str);
  413.                tmp_str[0] = '\0';
  414.                CtoPstr(input_str);
  415.                sprintf(tmp_str, "%#s", input_str);
  416.                argc = parse(tmp_str, input_str, &argv);
  417.             }
  418.         else
  419.         {
  420.             argc = ccommand(&argv);
  421.             }
  422.     SetVol(0L, vRefNum);
  423.       _atexit(gnuucp_cleanup);
  424.     debug = -1;        /* Let read_params set it if -x doesn't */
  425.     ttynam[0] = '\0';
  426.     /* FIXME, use getopt */
  427.     /* scan command line arguments, kinda kludgy but it works */
  428.     for (i = 1; i < argc; i++) {
  429.         if (argv[i][0] != '-')
  430.             break;
  431.         switch (argv[i][1]) {
  432.  
  433.         case 'w':
  434.             f_wait++;
  435.             printf("uucico: will wait for call after outbound\n");
  436.             break;
  437.  
  438.         case 'x':
  439.             debug = atoi(&argv[i][2]);
  440.             break;
  441.  
  442.         case 'S':
  443.             ignore_time_restrictions++;
  444.         case 's':
  445.             poll_sys = &argv[i][2];
  446.             ontheline = 0;
  447.             break;
  448.  
  449.         case 'e':
  450.             loop++;
  451.             printf("uucico: endless loop mode\n");
  452.             break;
  453.  
  454.         case 'C':
  455.             uuControl = &argv[i][2];
  456.             break;
  457.  
  458.         /* Is -t needed for MSDOS?  Why?  -- hoptoad!gnu */
  459.         case 't':
  460.             curtemp++;
  461.             printf("uucico: using ~uutemp.$$$ for temp file\n");
  462.             break;
  463.         }
  464.     }
  465.  
  466.     /* If argument provided, use it as name of comm port */
  467.     if (i < argc) {
  468.         ontheline = 0;
  469.         strcpy(ttynam, argv[i]);
  470.     }
  471.     if (read_params(uuControl) == FAIL)
  472.         exit(EXIT_ERR);        /* Read control file */
  473.     if (chdir(Spool)) {
  474.         perror("Can't chdir to Spool directory");
  475.         exit(EXIT_ERR);
  476.     }
  477.     /*
  478.      * If running via getty/login, our debug stdout had better
  479.      * go to a file, not to the usual stdout!
  480.      */
  481. #ifdef HAS_GETTY
  482.     if (DEBUG_LEVEL(0) && ontheline) {
  483.         {
  484.             char *mode;
  485.             if (AppendLogfile == TRUE) 
  486.                 mode = "a";
  487.             else
  488.                 mode = "w";
  489.             freopen(Loginfile, mode, stdout);
  490.             }
  491.     }
  492. #endif
  493.     /* setbuf(stdout, (char *)NULL);    Unbuffered debug output */
  494.  
  495.     /* If we are running with minimal debugging
  496.        then don't pause at program termination
  497.     */
  498.     if (debug == 0) console_options.pause_atexit = false;
  499.     
  500.     /* Timestamp the long debug log */
  501.     if (DEBUG_LEVEL(0)) {
  502.         printf("\n\n\n\ngnuucp log on tty '%s' starting %s\n",
  503.             ttynam, time_and_pid());
  504.     }
  505.  
  506.     /* Log our presence so we humans reading the logs can find the
  507.        entries created by gnuucp. */
  508.     logit("GNUUCP", version);
  509.     if (poll_sys) {
  510.         if (*poll_sys == '\0') poll_sys = (char *)NULL;
  511.         do_rmail_queue();
  512.         start_time = clock();
  513.         call_system(poll_sys);
  514.         end_time = clock();
  515.         sprintf(tmp_str, "%ld chars/sec", 
  516.             (total_data_processed*CLOCKS_PER_SEC)/(end_time-start_time));
  517.         logit("Transfer rate:", tmp_str);
  518.         uuxqt(debug);
  519.         if (!f_wait) goto end;
  520.     }
  521.  
  522.     do {
  523.         /*
  524.          *  Set up serial channel, wait for incoming call. 
  525.          */
  526.  
  527.         do_rmail_queue();
  528.         DEBUG(0, "\nRestarting\n", 0);
  529.         /* openline(ttynam, 0);  FIXME, let user specify baudrate */
  530.  
  531.         start_time = clock();
  532.         retval = do_session(ontheline, ttynam);
  533.  
  534.         end_time = clock();
  535.         sprintf(tmp_str, "%ld chars/sec", 
  536.             (total_data_processed*CLOCKS_PER_SEC)/(end_time-start_time));
  537.         logit("Transfer rate:", tmp_str);
  538.         uuxqt(debug);
  539.         DEBUG(0, "\nEnd of call\n", 0);
  540.  
  541.     } while (loop && !ontheline && (retval == EXIT_OK));
  542.  
  543. end:
  544.     return(0);
  545.     }
  546.  
  547. debuggit()
  548. {
  549. #ifdef UNIX
  550.     if (fork() == 0)
  551.         execlp("forktest", "testing file descriptors", 0);
  552. #endif
  553. }
  554.  
  555. /*
  556.  * Handle a single set of uucp expect/send strings.
  557.  *
  558.  * Return SUCCESS if the expect string is found, or FAIL if not.
  559.  */
  560. #ifdef IGNORE
  561. sendexpect(expect, send)
  562.     register char *expect;
  563.     char *send;
  564. {
  565.     char *dasher, *dancer;
  566.  
  567.     while ((dasher = strchr(expect, '-'))
  568.         && (dancer = strchr(dasher+1, '-'))) {
  569.         /* Two dashes found -- we have an expect-send-expect string */
  570.         *dasher = '\0'; *dancer = '\0';
  571.         if (instr(expect, dasher-expect,1) == SUCCESS)
  572.             goto sendit;
  573.         xlat_str(dasher+1);        /* Send the alternative */
  574.         expect = dancer + 1;        /* Try next pair, if any */
  575.     }
  576.         
  577.     if (expect[0] != '"' || expect[1] != '"' || expect[2] != '\0')
  578.         if (instr(expect, strlen(expect), 1))
  579.             return FAIL;
  580.  
  581. sendit:
  582.     if (send) {
  583.         xlat_str(send);
  584.     }
  585.     return SUCCESS;
  586. }
  587. #endif
  588.  
  589. sendexpect(expect, send)
  590.     register char *expect;
  591.     char *send;
  592. {
  593.     char *dasher, *dancer;
  594.     /* Is there an initial -\r- string?? */
  595.     if (expect[0] == '-' && strchr(expect+1,'-'))
  596.         {
  597.             dasher = strchr(expect, '-');
  598.             dancer = strchr(dasher+1, '-');
  599.             *dasher = '\0'; *dancer = '\0';
  600.             xlat_str(dasher+1);
  601.             expect = dancer+1;
  602.             }
  603.     while ((dasher = strchr(expect, '-'))
  604.         && (dancer = strchr(dasher+1, '-'))) {
  605.         /* Two dashes found -- we have an expect-send-expect string */
  606.         *dasher = '\0'; *dancer = '\0';
  607.         if (instr(expect, dasher-expect, 1) == 0)
  608.             goto sendit;
  609.         xlat_str(dasher+1);        /* Send the alternative */
  610.         expect = dancer + 1;        /* Try next pair, if any */
  611.     }
  612.         
  613.     if (expect[0] != '"' || expect[1] != '"' || expect[2] != '\0')
  614.         if (instr(expect, strlen(expect), 1))
  615.             return FAIL;
  616.  
  617. sendit:
  618.     if (send) {
  619.         xlat_str(send);
  620.     }
  621.     return SUCCESS;
  622. }
  623.  
  624. /*
  625.  * translate embedded escape characters in a "send" string, and send 'em.
  626.  */
  627. xlat_str(msg)
  628.     register char    *msg;
  629. {
  630.     register int    i  = 0,
  631.         j  = 0;
  632.     int    cr = 1;
  633.     static char out[MAX_STRING+SLOP];
  634.  
  635.     gnusleep(1);        /* Old uucp did this, guess we'd better */
  636.     while (msg[i]) {
  637.         if (msg[i] == '\\') {
  638.             switch (msg[++i]) {
  639.             case 'r':            /* carriage return */
  640.                 out[j++] = 0x0d;
  641.                 break;
  642.             case 'b':            /* Send Break */
  643.                 send_break ();
  644.                 break;
  645.             case 'n':            /* line feed */
  646.                 out[j++] = 0x0a;
  647.                 break;
  648.             case '\\':           /* back slash */
  649.                 out[j++] = '\\';
  650.                 break;
  651.             case 't':            /* tab */
  652.                 out[j++] = '\t';
  653.                 break;
  654.             case 'd':            /* delay */
  655.                 /* Print output, delay, resume */
  656.                 twrite(out, j);
  657.                 j = 0;
  658.                 if (DEBUG_LEVEL(2)) printf("(sleep)  ");
  659.                 gnusleep(1);
  660.                 break;
  661.             case 's':            /* space */
  662.                 out[j++] = ' ';
  663.                 break;
  664.             case 'c':            /* no CR at end */
  665.                 cr = 0;
  666.                 break;
  667.             default:            /* don't know so skip it */
  668.                 break;
  669.             }
  670.             i++;
  671.         } else {
  672.             out[j++] = msg[i++];
  673.         }
  674.     if (j) twrite(out, j);
  675.     j = 0;
  676.     }
  677.  
  678.     if (cr)
  679.         out[j++] = 0x0d;
  680.     if (j) twrite(out, j);
  681. }
  682.  
  683. /*
  684.  * Line finder.  Given an "ACU" specifier from the L.sys file,
  685.  * and a baud rate, determine a file name that we should try to lock
  686.  * and open.
  687.  *
  688.  * On initial call, pass in (struct port *)0 as the port argument.
  689.  * On subsequent calls, we skip past that port, which is presumed to
  690.  * have been tried and failed.
  691.  *
  692.  * If we find a matching entry in the config file ports database,
  693.  * return it; else return a dummy entry that might let the user get
  694.  * by with specifying just the device name for hardwired lines.
  695.  */
  696. #ifdef FIXME
  697. static struct port dummyport = {
  698.     "",                /* Name -- filled in later */
  699.     "none",                /* Modem type */
  700.     "",                /* Dev name -- filled in later */
  701.     0,                /* Baud rate -- filled in later */
  702.     (struct port *)0};        /* Chain */
  703. #endif FIXME
  704.  
  705. struct port *
  706. findport(name, p)
  707.     char    *name;
  708.     struct port *p;
  709. {
  710.  
  711.     if (p == 0)
  712.         p = ports;
  713.     else
  714.         p = p->chain;
  715.  
  716.     for ( ; p; p = p->chain) {
  717.         if (strcmp(name, p->devname) == 0)
  718.             return p;
  719.     }
  720.  
  721.     return p;            /* Didn't find it -- return null ptr */
  722. }
  723.     
  724. struct port *
  725. pickport(acu, baud, p)
  726.     char    *acu;
  727.     long    baud;
  728.     struct port *p;
  729. {
  730.  
  731.     if (p == 0)
  732.         p = ports;
  733.     else
  734.         p = p->chain;
  735.  
  736.     for ( ; p; p = p->chain) {
  737.         if (baud == p->baud &&
  738.             strcmp(acu, p->portname) == 0)
  739.             return p;
  740.     }
  741.  
  742.     return p;            /* Didn't find it -- return null ptr */
  743.  
  744. #ifdef FIXME
  745.     strcpy(dummyport.portname, acu);    /* Fill in acu name */
  746.     strcpy(dummyport.devname,  acu);    /* Fill in acu name */
  747.     dummyport.baud = baud;
  748.     return &dummyport;
  749. #endif FIXME
  750. }
  751.  
  752.  
  753. /*
  754.  * Simple dialer routine.  Needs replacement with a full blown
  755.  * script driven dialer.  Next week maybe :-).  FIXME.
  756.  */
  757. dial_nbr(port, nbr)
  758.     struct port *port;
  759.     char  *nbr;
  760. {
  761.     char  dial[256];
  762.     char  conn[256];
  763.     clock_t start_time;
  764.     clock_t end_time;
  765.     SerStaRec serSta;
  766.     int   i;
  767.  
  768.     /* Hardwired lines do no dialing. */
  769.     if (strcmp("none", port->modemname) == 0)
  770.         return SUCCESS;
  771.  
  772.     /* If it doesn't say hayes, we can't dial it. */
  773.     if (strcmp("hayes", port->modemname) != 0)
  774.         return FAIL;
  775.  
  776.     sprintf(dial, "%s %s\r", DialPrefix, nbr);
  777.     if (DEBUG_LEVEL(2))
  778.         printf("dialing %s at %ld baud: %s\n",
  779.             port->devname, port->baud, dial);
  780.     /* SerStatus(inRefNum, &serSta); */
  781.     twrite(dial, strlen(dial));
  782.     gnusleep(5);
  783.     start_time = clock();
  784.     /* gnusleep(ConnectWait); */
  785.     /* while (!serSta.ctsHold)
  786.             {
  787.                 HandleEvents();
  788.                 SerStatus(inRefNum, &serSta);
  789.                 } */
  790. /* FIXME, we've got to be more flexible here... */
  791. /* sprintf(conn, "CONNECT %s\r", baud);*/
  792.     sprintf(conn, "CONNECT");
  793.  
  794.     
  795.     for (end_time = clock(), i = FAIL; 
  796.         (end_time - start_time)/CLOCKS_PER_SEC < ConnectWait;
  797.         end_time = clock())
  798.         {
  799.             i = instr(conn, strlen(conn), 7);
  800.             if (i == SUCCESS)
  801.                 break;
  802.             }
  803.     /* FIXME, scan out the baud rate, or whatever's before the CR, here.
  804.      */
  805.  
  806.     if (i == SUCCESS) 
  807.         gnusleep(2);    /* Let the line settle. */
  808.  
  809.     return (i);
  810. }
  811.  
  812. /*
  813.  * Call a specific system, or all systems that have work pending.
  814.  */
  815. call_system(sys)
  816.     char    *sys;
  817. {
  818.     FILE    *lsys;
  819.     char    buf[MAX_LSYS];
  820.     char     tmp_str[256];
  821.     char    sysnam[MAX_HOST];
  822.     char    prev_name[MAX_HOST];
  823.     int    called = FAIL;
  824.  
  825.     /*
  826.      * Unix uucico just reads the directory, and calls the systems
  827.      * in the order of the files in the directory.  We want more
  828.      * control than that, though I'm not sure that L.sys order is
  829.      * best either.  For example, in the first call after 11PM,
  830.      * I'd like to call the sites that haven't been callable before
  831.      * 11PM first, and finish up with the ones I've been able to call
  832.      * all day.  FIXME.
  833.      */
  834.     /* sprintf(tmp_str,"%s:%s", Spool, "L.sys"); */
  835.     if (! (lsys =fopen(Sysfile, "r"))) {
  836.         DEBUG(0, "uucico: can't open L.sys, errno %d\n", errno);
  837.         return 0;
  838.     }
  839.     sysnam[0] = '\0';        /* Initially, no previous sys */
  840.  
  841.     /* Once per system in L.sys... */
  842.     /* FIXME, handle continuation lines (trailing "\") */
  843.     while (get_sysline(buf, sizeof(buf), lsys))
  844.         {
  845.         /*
  846.          * Grab the system name.  If same as previous, and
  847.          * the previous call worked, skip it.
  848.          */
  849.         strcpy(prev_name, sysnam);
  850.         (void) sscanf((char *)buf, "%s", sysnam);
  851.         if (!strcmp(sysnam, prev_name)) {
  852.             if (called == SUCCESS) continue;
  853.         }
  854.  
  855.         /*
  856.          * If a system name was specified, skip til we find it
  857.          * If none was specified, only call if there is work.
  858.          */
  859.         if (sys) {
  860.             if (0 != strcmp(sys, sysnam))
  861.                 continue;
  862.         } else {
  863.             DEBUG(4,"searching for outbound to %s\n", sysnam);
  864.  
  865.             if (!work_scan(sysnam, "C")) {
  866.                 DEBUG(3,"no work for %s\n", sysnam);
  867.                 called = SUCCESS;    /* Don't try further */
  868.                 continue;
  869.             }
  870.  
  871.             DEBUG(3, "found work for %s\n", sysnam);
  872.         }
  873.  
  874.         called = call_sysline(buf);
  875.  
  876.         if (called == SUCCESS && sys) break;
  877.     }
  878.  
  879.     fclose(lsys);
  880.     if (called == FAIL && sys)
  881.         DEBUG(0, "Could not call system %s\n", sys);
  882.     return 0;
  883. }
  884.  
  885. char *get_sysline(buf, size, lsys)
  886. char *buf;
  887. int size;
  888. FILE *lsys;
  889. {
  890.     char *start;
  891.     start = buf;
  892.     buf[0] = '\0';
  893.     while (fgets(buf, size, lsys))
  894.     {
  895.         if (buf[strlen(buf)-1] == '\n')
  896.             {
  897.                 buf[strlen(buf)-1] = '\0';
  898.                 }
  899.         if (buf[0] == '#')
  900.             continue;
  901.         if (buf[strlen(buf)-1] == '\\')
  902.             {
  903.                 buf[strlen(buf)-1] = ' ';
  904.                 buf = buf+strlen(buf);
  905.                 continue;
  906.                 }
  907.         return(start);
  908.         }
  909.     if (strlen(buf) == 0) 
  910.         return(NULL);
  911.     return(start);
  912.     }
  913.  
  914. /*
  915.  * Call out to a system, given its L.sys line.
  916.  */
  917. int
  918. call_sysline(lsysline)
  919.     char    *lsysline;
  920. {
  921.     char    tempname[MAX_HOST + 30 + SLOP],
  922.         *sysnam,
  923.         *times,
  924.         *acu,
  925.         *sbaud,
  926.         *telno,
  927.         *send,
  928.         *expect;
  929.     struct port *port;
  930.     char    logbuf[MAX_HOST+30+SLOP];
  931.     static char    msgbuf[MAX_STRING+SLOP];
  932.     long    baud;
  933.  
  934.     who[0] = '-'; who[1] = '\0';    /* No user now (for logit) */
  935.  
  936.     /* FIXME, use the values it is ignoring here */
  937.     sysnam = strtok(lsysline, " ");
  938.     times = strtok((char *)NULL, " ");    /* Time */
  939.     acu = strtok((char *)NULL, " ");    /* ACU */
  940.     sbaud = strtok((char *)NULL, " ");    /* Baud */
  941.     telno = strtok((char *)NULL," ");    /* phone */
  942.  
  943.     strcpy(host_name, sysnam);
  944.  
  945.     if (!ignore_time_restrictions) {
  946.         /* FIXME, check the time parameter and return FAIL if
  947.          * it does not allow calls now.  Meanwhile, bounce
  948.          * all calls unless -S is specified. */
  949.         logit("WRONG TIME TO CALL", sysnam);
  950.         return FAIL;
  951.     }
  952.  
  953.     /* baud = atoi(sbaud); */
  954.     sscanf(sbaud, "%ld", &baud);
  955.     port = (struct port *)0;
  956.     for (;;) {
  957.         port = pickport(acu, baud, port);
  958.         if (!port)
  959.             return FAIL;        /* Tried them all */
  960.         DEBUG(2, "Opening outgoing line %s\n", port->devname);
  961.         if (openout(port) != SUCCESS) {
  962.             logit("CAN'T USE", port->devname);
  963.         } else {
  964.             break;
  965.         }
  966.     }
  967.  
  968.     sprintf(logbuf, "%s %s %d", host_name, port->devname, port->baud);
  969.     DEBUG(1, "Trying %s\n", logbuf);
  970.  
  971.     if ((dial_nbr(port, telno) != SUCCESS)) {
  972.         ttyunlock();
  973.         logit("FAILED", logbuf);
  974.         return FAIL;
  975.     }
  976.  
  977.     logit("DIALED", logbuf);
  978.  
  979.     /*
  980.      * Process send-expect strings.
  981.      */
  982.     while (expect = strtok((char *)NULL, " ")) {
  983.         send = strtok((char *)NULL, " ");
  984.         if (sendexpect(expect, send) != SUCCESS)
  985.             goto bort1;
  986.     }
  987.  
  988.     /*
  989.      * FIXME, there should be a way to detect login/passwd
  990.      * failure here and keep doing the script rather than
  991.      * continuing to expect Shere at another login: prompt.
  992.      */
  993.     sprintf(logbuf, "call to %s", host_name);
  994.     logit("SUCCEEDED", logbuf);
  995.  
  996.     /* wait for Shere message, grab whatever is sent, send response */
  997.     {
  998.         char *msgptr;
  999.         sprintf(tempname, "Shere");
  1000.         /* if (instr(tempname, strlen(tempname), 1))
  1001.             goto bort1; */
  1002.     again:
  1003.         if (getstring(msgbuf) != SUCCESS)
  1004.             goto bort1;
  1005.         /* printf("MSGBUF: %s\n", msgbuf); */
  1006.         msgptr = strstr(msgbuf, tempname);
  1007.         /* printf("msgptr: %s\n", msgptr); */
  1008.         if (msgptr == NULL) goto again;
  1009.         msgptr = msgptr + strlen(tempname);
  1010.         if (msgptr[0] == '=')
  1011.             {
  1012.                 if (msgptr[1] != '\0')
  1013.                     {
  1014.                         if (!!strcmp(host_name, msgptr+1)) 
  1015.                             {
  1016.                                 logit("WRONG HOST", msgptr+1);
  1017.                                 goto bort1;
  1018.                                 }
  1019.                         }
  1020.                 } 
  1021.             else 
  1022.              {
  1023.                  if (msgptr[0] != '\0') 
  1024.                     {
  1025.                         DEBUG(0, "strange Shere%s\n", msgbuf);
  1026.                         }
  1027.                 }
  1028.         /* printf("MSGBUF+ofs+count: %s\n", msgptr); */
  1029.         }
  1030.     sprintf(tempname, "\20S%s\0", Myname);
  1031.     twrite(tempname, strlen(tempname)+1); /* Including null */
  1032.  
  1033.     /* wait for ok message */
  1034.     if (getstring(msgbuf) != SUCCESS)
  1035.         goto bort1;
  1036.     if (msgbuf[0] != 'R')
  1037.         goto bort1;
  1038.     if (strcmp("OK", msgbuf+1)) {
  1039.         logit("HANDSHAKE FAILED", msgbuf+1);
  1040.         goto bort1;
  1041.     }
  1042.  
  1043.     /*
  1044.      * Get Protocol string, make sure it supports our protocol.
  1045.      * FIXME, should scan our table versus theirs.
  1046.      */
  1047.     if (getstring(msgbuf) != SUCCESS)
  1048.         goto bort1;
  1049.     if (msgbuf[0] != 'P')
  1050.         goto bort1;
  1051.     if (!strchr(msgbuf+1, curproto->p_id)) {
  1052.         logit("PROTOCOL UNSUPPORTED", msgbuf);
  1053.         goto bort1;
  1054.     }
  1055.     twrite(msgi3, sizeof(msgi3)-1);    /* FIXME, build real reply */
  1056.  
  1057.     if (turnon(1))
  1058.         goto bort1;
  1059.  
  1060.     logit("OK", "startup");
  1061.  
  1062.     top_level(1);
  1063.     hangup(port);
  1064.     return SUCCESS;
  1065.  
  1066. bort1:
  1067.     hangup(port);
  1068.     return FAIL;
  1069. }
  1070.  
  1071. /* Handle a single uucp [slave] login session */
  1072. /* =Ned Horvath= ech@pegasus.att.com
  1073.    modified this to be compatible with SVR3 wrt the
  1074.    line end characters.
  1075. */
  1076.  
  1077. do_session(ontheline, ttynam)
  1078.     int ontheline;
  1079.     char *ttynam;
  1080.     {
  1081.     static char    trash[MAX_STRING+SLOP];    /* Incoming trash buffer */
  1082.     char tmp_str[256];
  1083.     char ch;
  1084.     SerStaRec serSta;
  1085.     struct port *p;
  1086.     p = (struct port *)0;
  1087.     if (!ontheline) {
  1088.         serSta.ctsHold = 0;
  1089.         p = findport(ttynam, p);
  1090.         if (p == (struct port *)NULL)
  1091.             {
  1092.                 logit("Can't find port to", "open");
  1093.                 return(EXIT_ERR);
  1094.                 }
  1095.         openin(p);
  1096.         /* Look for signs of life on line */
  1097.         while (instr("CONNECT",7, 7));
  1098.         twrite(msgo0,sizeof(msgo0)-1);
  1099.             sprintf(tmp_str, "%s", UUCPlogname);  /* ech: was "%s\r" */
  1100.             if (instr(tmp_str,strlen(tmp_str), 1)) 
  1101.                 {
  1102.             badLogname:
  1103.                     printf("uucico: invalid login name\n");
  1104.                     goto bort;
  1105.                     }
  1106.             switch (xgetc()) 
  1107.                 { /* ech: verify \r or \n */
  1108.                     case '\r':
  1109.                     case '\n':
  1110.                         break;
  1111.                     default:
  1112.                         goto badLogname;
  1113.                     }
  1114.             /* output password request, verify password */
  1115.             twrite(msgo1,sizeof(msgo1)-1);
  1116.             sprintf(tmp_str, "%s", UUCPpasswd);   /* ech: was "%s\r" */
  1117.             if (instr(tmp_str,strlen(tmp_str), 1)) 
  1118.                 {
  1119.                     badPW:
  1120.                         printf("uucico: invalid password\n");
  1121.                         goto bort;
  1122.                     }
  1123.             switch (xgetc()) 
  1124.                 {   /* ech: verify \r or \n */
  1125.                     case '\r':
  1126.                     case '\n':
  1127.                         break;
  1128.                     default:
  1129.                         goto badPW;
  1130.                     }
  1131.         printf("uucico: correct login\n");
  1132.     }
  1133.     if (p == (struct port *)NULL)
  1134.     {
  1135.         logit("Can't find port to", "open");
  1136.         return(EXIT_ERR);
  1137.         }
  1138.     /* output here message, wait for response */
  1139.     sprintf(tmp_str, "\20Shere=%s", Myname);
  1140.     twrite(tmp_str,strlen(tmp_str)+1); /* Include NULL at end */
  1141. /* FIXME, handle this kludge */
  1142. /*    if (instr(msgi2,sizeof(msgi2)-1), 1) */
  1143.     if (getstring(trash) != SUCCESS || trash[0] != 'S')
  1144.         goto bort;
  1145.  
  1146.     strcpy (host_name, strtok(trash, "\20S "));
  1147.  
  1148.     /* output ROK message, output protocol request, wait for response */
  1149.     twrite(msgo3,sizeof(msgo3)-1);
  1150.  
  1151.     /* FIXME, make the protocol list here, and use it */
  1152.     twrite(msgo3a,sizeof(msgo3a)-1);
  1153.     if (getstring(trash) != SUCCESS) goto bort;
  1154.     if (!strchr(trash+1, curproto->p_id)) 
  1155.         {
  1156.             logit("PROTOCOL UNSUPPORTED", trash);
  1157.             goto bort;
  1158.             }
  1159.     /* if (instr(msgi3,sizeof(msgi3)-1), 1)
  1160.         goto bort; */
  1161.     if (turnon(0)) goto bort;
  1162.     logit("OK", "startup");
  1163.     top_level(0);        /* Returns SUCCESS or FAIL, we dont care */
  1164.  
  1165. bort:
  1166.     hangup(p);
  1167.     printf("uucico: call complete\n");
  1168.     return(EXIT_OK);
  1169. }
  1170.  
  1171. /*
  1172.  * Handle transactions "at top level", as Unix uucp's debug log says.
  1173.  *
  1174.  * As master, we scan our queues for work and send requests to the
  1175.  * other side.  When done, we send a hangup request and switch to slave mode.
  1176.  *
  1177.  * As slave, we accept requests from the other side; when it is done,
  1178.  * it sends a hangup request, and we switch to master mode, if we have
  1179.  * any work queued up for that system.
  1180.  *
  1181.  * This repeats as long as either side has work to do.  When all the
  1182.  * queued work is done, we agree to hang up, terminate the packet protocol,
  1183.  * and return to the caller.  (We still haven't hung up the phone line yet.)
  1184.  *
  1185.  * A curious feature of the hangup protocol is that it is not a simple
  1186.  * question-answer.  The master says "H", asking about hangup.  The
  1187.  * slave responds "HY" saying OK.  The master then says "HY" also,
  1188.  * then both of them hang up.  Maybe this is to make sure the first HY
  1189.  * got ack'ed?  Anyway, an "H" is reported as HANGUP and an "HY" as
  1190.  * HANGNOW.  After we send an HY, we go back to listening for commands;
  1191.  * if the master sends something other than HY, we'll do it.
  1192.  */
  1193. #define    HANGUP    2        /* Signal to switch master/slave roles */
  1194. #define    HANGNOW    3        /* Signal to hang up now */
  1195. #define    COPYFAIL    4    /* File copy failed */
  1196.  
  1197. int
  1198. top_level(master_mode)
  1199.     int master_mode;
  1200. {
  1201.     char    buf[MAXMSGLEN];        /* For hangup responses */
  1202.  
  1203.     if (master_mode) {
  1204.         (void) work_scan(host_name, "C"); /* Kick off queue scan */
  1205.         goto master;
  1206.     }
  1207.  
  1208.     for (;;) {
  1209.         /* Slave side */
  1210.     slave:
  1211.         for (;;) {
  1212.             DEBUG(4, "*** TOP *** - slave\n", 0);
  1213.             switch (do_one_slave()) {
  1214.             case SUCCESS:
  1215.                 break;
  1216.             case FAIL:
  1217.                 return FAIL;
  1218.             case HANGUP:
  1219.                 if (work_scan(host_name, "C")) {
  1220.                     if (wrmsg('H', "N", fdtty))
  1221.                         return FAIL;
  1222.                     goto master;
  1223.                 } else {
  1224.                     if (wrmsg('H', "Y", fdtty))
  1225.                         return FAIL;
  1226.                     break;
  1227.                 }
  1228.             case HANGNOW:
  1229.                 goto quit;
  1230.             }
  1231.         }
  1232.     
  1233.         /* Master side */
  1234.     master:
  1235.         for (;;) {
  1236.             DEBUG(4, "*** TOP *** - master\n", 0);
  1237.             switch (do_one_master()) {
  1238.             case SUCCESS:
  1239.                 break;
  1240.             case FAIL:
  1241.                 return FAIL;
  1242.             case HANGUP:
  1243.                 /* We wrote an H command, what's the resp? */
  1244.                 if ((long)rdmsg(buf, fdtty) != (long)SUCCESS)
  1245.                     return FAIL;
  1246.                 if (buf[0] != 'H')
  1247.                     return FAIL;
  1248.                 if (buf[1] == 'N')
  1249.                     goto slave;
  1250.                 else {
  1251.                     /* Write the final HY */
  1252.                     if (wrmsg('H', "Y", fdtty))
  1253.                         return FAIL;
  1254.                     goto quit;
  1255.                 }
  1256.             }
  1257.         }
  1258.     }
  1259.  
  1260. quit:
  1261.     /* Shut down the packet protocol */
  1262.     turnoff();
  1263.  
  1264.     /* Write the closing sequence */
  1265.     twrite(msgo4, sizeof(msgo4)-1);
  1266.     (void) instr(msgi4, sizeof(msgi4)-1, 1);
  1267.  
  1268.     twrite(msgo4, sizeof(msgo4)-1);
  1269.  
  1270.     logit("OK", "conversation complete");
  1271.  
  1272.     return SUCCESS;   /* Go byebye */
  1273. }
  1274.  
  1275. /*
  1276.  * We are slave; get a command from the other side and execute it.
  1277.  *
  1278.  * Result is SUCCESS, FAIL, HANGUP, or HANGNOW.
  1279.  */
  1280. int
  1281. do_one_slave()
  1282. {
  1283.     char msg[MAXMSGLEN];        /* Master's message to us */
  1284.  
  1285.     /* Get master's command */
  1286.     if ((long)rdmsg(msg, fdtty) != (long)SUCCESS)
  1287.         return FAIL;
  1288.  
  1289.     /* Print it for easy debugging */
  1290.     DEBUG(4,"Command: %s\n\n", msg);
  1291.  
  1292.     switch (msg[0]) {
  1293.  
  1294.     case 'S':
  1295.         if (msg[1] != ' ') break;
  1296.         return host_send_file(msg);
  1297.  
  1298.     case 'R':
  1299.         if (msg[1] != ' ') break;
  1300.         return host_receive_file(msg);
  1301.  
  1302.     case 'X':
  1303.         /* Cause uuxqt to run (on certain files?)
  1304.          * See Protocol.doc for sketchy details.
  1305.          */
  1306.         break;
  1307.  
  1308.     case 'H':
  1309.         if (msg[1] == '\0') return HANGUP;
  1310.         if (msg[1] == 'Y')  return HANGNOW;
  1311.         if (msg[1] == 'N')  return SUCCESS;    /* Ignore HN to slave */
  1312.         break;
  1313.  
  1314.     }
  1315.  
  1316.     /* Unrecognized packet from the other end */
  1317.     DEBUG(0, "Bad control packet refused: %s\n", msg);
  1318.     if (yesno(msg[0], 0, 0))    /* FIXME: return error code */
  1319.         return FAIL;
  1320.     return SUCCESS;
  1321. }
  1322.  
  1323. /*
  1324.  * Do one piece of work as master.
  1325.  *
  1326.  * FIXME:  we don't handle the flags, e.g. -c, properly!
  1327.  */
  1328. int
  1329. do_one_master()
  1330. {
  1331.     FILE    *fd;
  1332.     char    *sname;
  1333.     char    cmnd[10];        /* Command character */
  1334.     char     buf[256];
  1335.     char    tmp_str[256];
  1336.     int    fail;
  1337.     int    num;
  1338.     char    notify[NAMESIZE];    /* A bit large...FIXME */
  1339.             /* FIXME: do the notify stuff */
  1340.  
  1341.     sname = work_next();
  1342.     if (!sname) {
  1343.         /* No more work, time to hang up. */
  1344.         if (wrmsg('H', "", fdtty))
  1345.             return FAIL;
  1346.         return HANGUP;
  1347.     }
  1348.  
  1349.     DEBUG(4, "Request file %s\n", sname);
  1350.     sprintf(tmp_str,"%s:%s", Spool, munge_filename(sname));
  1351.     fd = fopen(tmp_str, "rb");
  1352.     if (fd == NULL) {
  1353.         DEBUG(0, "uucico: couldn't open %s\n", sname);
  1354.         /* fail = local_receive_file(buf); */
  1355.         fail = local_receive_file();
  1356.         return SUCCESS;
  1357.     }
  1358.     setvbuf(fd, NULL, _IOFBF, 4096);
  1359.     while (fgets(buf, sizeof buf, fd)) {
  1360.         DEBUG(4, "Queued request: %s", buf);
  1361.         if (buf[1] != ' ') goto badnum;
  1362.         num = sscanf((char *)buf, "%s %s %s %s %s %s %o\n",
  1363.             cmnd, srcnam, dstnam, who, flags, temp, &mode, notify);
  1364.  
  1365.         switch (cmnd[0]) {
  1366.         case 'S':
  1367.             if (num < 7 || num > 8) goto badnum;
  1368.             fail = local_send_file(buf);
  1369.             break;
  1370.  
  1371.         case 'R':
  1372.             if (num != 5) goto badnum;
  1373.             break;
  1374.  
  1375.         default: badnum:
  1376.             DEBUG(0, "Unknown/invalid queued request: %s\n",
  1377.                  buf);
  1378.             goto badline;
  1379.         }
  1380.  
  1381.         /* FIXME, what does uucp do if one of N xfers fails? */
  1382.         if (fail == FAIL) {
  1383.             /* protocol over the wire is failing */
  1384.             fclose(fd);
  1385.             return FAIL;
  1386.         }
  1387.         if (fail) {
  1388.     badline:
  1389.             logit("ERROR IN WORK FILE", sname);
  1390.             logit("BAD LINE IS", buf);
  1391.         }
  1392.     }
  1393.     fclose(fd);
  1394.     /* Zap the queue file */
  1395.     sprintf(tmp_str,"%s:%s", Spool, munge_filename(sname));
  1396.     fail = remove(tmp_str);
  1397.     if (fail != 0) {
  1398.         logit("CAN'T REMOVE WORK FILE", sname);
  1399.         DEBUG(0, "Can't remove, errno %d\n", errno);
  1400.     } else {
  1401.         DEBUG(4, "Removed work file %s\n", sname);
  1402.     }
  1403.     return SUCCESS;
  1404. }
  1405.  
  1406. /* Send a "yes or no" packet with character 'c'. */
  1407. int
  1408. yesno(c, true, err)
  1409.     char c;
  1410.     int true;
  1411.     int err;
  1412. {
  1413.     char buf[20];
  1414.  
  1415.     buf[0] = true? 'Y': 'N';
  1416.     buf[1] = 0;
  1417.     if (err && !true) 
  1418.         sprintf(buf+1,"%d", err);
  1419.  
  1420.     return wrmsg(c, buf, fdtty);
  1421. }
  1422.  
  1423. /*
  1424.  * Master wishes to send a file to us -- we receive it.
  1425.  * Return 1 to abort the call, 0 to continue.
  1426.  */
  1427. int
  1428. host_send_file(msg)
  1429. char  *msg;
  1430. {
  1431.     FILE *fddsk;            /* Disk file pointer */
  1432.     char tmp_str[256];
  1433.     char    cmnd[10];        /* Command character */
  1434.  
  1435.     sscanf((char *)msg,"%s %s %s %s %s %s %o",
  1436.         cmnd, srcnam, dstnam, who, flags, temp, &mode);
  1437.     logit("-> REQUESTED", msg);
  1438.     strcpy (dstnam, munge_filename(dstnam));  /* Translate to local name */
  1439.     strcpy (temp, temp_filename(dstnam));     /* Create a handy temp file */
  1440.  
  1441.     /* FIXME: deal with file modes now that we fopen. */
  1442.     sprintf(tmp_str, "%s:%s", Spool, temp);
  1443.     fddsk = fopen(tmp_str, "wb" /*, mode|0600 */);
  1444.     if (fddsk == NULL) {
  1445.         /* Can't open file -- send error response */
  1446.         if (DEBUG_LEVEL(0)) {
  1447.             printf(
  1448.             "Cannot open temp file %s (%s) for writing, errno=%d\n",
  1449.                 temp, dstnam, errno);
  1450.         }
  1451.         logit("-> REQUEST", "FAILED -- TEMP FILE");
  1452.         if (yesno('S', 0, 4))
  1453.             return FAIL;
  1454.         return SUCCESS;
  1455.     }
  1456.     setvbuf(fddsk, NULL, _IOFBF, 4096);
  1457.     
  1458.     /* FIXME: Are the above permissions right?? */
  1459.     /* FIXME: Should we create directories for the file? */
  1460.     if (yesno('S',1, 0))    /* Say yes */
  1461.         return 1;
  1462.  
  1463.     return receive_file(fddsk, temp, dstnam, srcnam);
  1464. }
  1465.  
  1466. /*
  1467.  * Master wants to Recieve a file from us -- we send it.
  1468.  * Return 1 to abort the call, 0 to continue.
  1469.  */
  1470. host_receive_file(msg)
  1471. char  *msg;
  1472. {
  1473.     FILE *fddsk;     /* Disk file descriptor */
  1474.     char tmp_str[256];
  1475.     int x;
  1476.     char    cmnd[10];        /* Command character */
  1477.  
  1478.     logit("<- REQUESTED", msg);
  1479.  
  1480.     sscanf((char *)msg,"%s %s %s",cmnd,srcnam,dstnam);
  1481.     strcpy (temp, munge_filename(srcnam));
  1482.     sprintf(tmp_str, "%s:%s", Spool, temp);
  1483.     fddsk = fopen(tmp_str, "rb");        /* Try to open the file */
  1484.     if (fddsk == NULL) {
  1485.         /* File didn't open, sigh. */
  1486.         if (DEBUG_LEVEL(0)) {
  1487.             printf("Cannot open file %s (%s) for reading, errno=%d\n",
  1488.                 temp, srcnam, errno);
  1489.         }
  1490.         logit("<- DENIED", "CAN'T OPEN");
  1491.         if (yesno('R', 0, 2))
  1492.             return 1;
  1493.         return 0;
  1494.     }
  1495.     setvbuf(fddsk, NULL, _IOFBF, 4096);
  1496.     
  1497.  
  1498.     if (yesno('R',1, 0))    /* Say yes */
  1499.         return 1;
  1500.  
  1501.     x = send_file(fddsk);
  1502.     switch (x) {
  1503.     default:
  1504.         return x;
  1505.  
  1506.     case COPYFAIL:
  1507.         /* We don't care if the copy failed, since the master
  1508.            asked for the file and knows the result. */
  1509.         return SUCCESS;
  1510.     }
  1511. }
  1512.  
  1513. /*
  1514.  * We, as master, want to Send a file.
  1515.  *
  1516.  * Return FAIL, SUCCESS, or COPYFAIL.
  1517.  * SUCCESS is returned either if the file was not found locally (local
  1518.  * error, and the queued transfer should be flushed) or if it was moved
  1519.  * successfully.  COPYFAIL indicates that the queued transfer should be
  1520.  * left queued, and later retried.  FIXME, there are several failure points
  1521.  * in the transaction (see Protocol.doc) and we need finer control here.
  1522.  */
  1523. int
  1524. local_send_file(workstr)
  1525.     char *workstr;
  1526. {
  1527.     char buf[MAXMSGLEN];    /* Used for both xmit and receive */
  1528.     FILE *fddsk;     /* Disk file descriptor */
  1529.     char tmp_str[256];
  1530.     int res, status;    /* Result and file removal status */
  1531.  
  1532.     /* WHY are temp and srcnam switched?  FIXME!  And no notify? */
  1533.     sprintf(buf,"S %s %s %s %s %s 0%o %s",
  1534.         temp, dstnam, who, flags, srcnam, mode, who);
  1535.  
  1536.     logit("<- REQUEST", buf);
  1537.  
  1538.     if (strchr(flags, 'c')) {
  1539.         strcpy(temp, munge_filename(srcnam));
  1540.     } else {
  1541.         strcpy(temp, munge_filename(temp));
  1542.     }
  1543.     sprintf(tmp_str, "%s:%s", Spool, temp);
  1544.     fddsk = fopen(tmp_str, "rb");
  1545.     if (fddsk == NULL) {
  1546.         /* FIXME -- handle queued request for nonexistent file */
  1547.         if (DEBUG_LEVEL(0))
  1548.             printf("Can't open file %s (%s), errno=%d\n",
  1549.                 temp, srcnam, errno);
  1550.         logit("<- NOT FOUND", temp);
  1551.         return COPYFAIL;    /* FIXME caller won't deal with this */
  1552.     }
  1553.     setvbuf(fddsk, NULL, _IOFBF, 4096);
  1554.     
  1555.     /* Tell the other side we want to send this file */
  1556.     if (wrmsg('S', buf+1, fdtty) != SUCCESS) {
  1557.         DEBUG(0, "problem sending request\n", 0);
  1558.         return FAIL;
  1559.     }
  1560.  
  1561.     /* See what they have to say about it */
  1562.     if ((long)rdmsg(buf, fdtty) != (long)SUCCESS)    
  1563.         return FAIL;
  1564.     if ((buf[0] != 'S') || (buf[1] != 'Y')) {
  1565.         logit("<- REQUEST DENIED", buf);
  1566.         return COPYFAIL;
  1567.     }
  1568.     res = send_file(fddsk);    /* FAIL, SUCCESS, or COPYFAIL */
  1569.  
  1570.     /* Delete the source file if it was just a copy */
  1571.     if (res != SUCCESS)
  1572.         return res;
  1573.     if (strchr(flags, 'c'))        /* If copied direct from source */
  1574.         return res;        /* ...just return. */
  1575.     status = remove(tmp_str);        /* Delete uucp's copy of the file */
  1576.     if (status != 0) {
  1577.         logit("<- CAN'T REMOVE SENT FILE", temp);
  1578.         DEBUG(0, "Can't remove, errno %d\n", errno);
  1579.     } else {
  1580.         DEBUG(4, "Removed sent file %s\n", temp);
  1581.     }
  1582.     return res;
  1583. }
  1584.  
  1585. /*
  1586.  * We want to Receive a file -- so we ask for it.
  1587.  * Return 1 to abort the call, 0 to continue.
  1588.  */
  1589. int
  1590. local_receive_file()
  1591. {
  1592.     char buf[MAXMSGLEN];
  1593.     FILE *fddsk;            /* Disk file pointer */
  1594.     char tmp_str[256];
  1595.  
  1596.     /* FIXME, test dest file access before we ask for it. */
  1597.  
  1598.     sprintf(buf,"R %s %s %s %s %s 0%o %s",
  1599.         srcnam, dstnam, who, flags, temp, mode, who);
  1600.  
  1601.     strcpy (dstnam, munge_filename(dstnam));  /* Translate to local name */
  1602.     strcpy (temp, temp_filename(dstnam));     /* Create a handy temp file */
  1603.  
  1604.     /* FIXME: deal with file modes now that we fopen. */
  1605.     /* FIXME: Are the above permissions right?? */
  1606.     /* FIXME: Should we create directories for the file? */
  1607.     sprintf(tmp_str, "%s:%s", Spool, temp);
  1608.     fddsk = fopen(tmp_str, "wb" /*, mode|060 */);
  1609.  
  1610.     if (fddsk == NULL) {
  1611.         /* Can't open temp file -- send error response */
  1612.         if (DEBUG_LEVEL(0)) {
  1613.             printf(
  1614.             "Cannot open temp file %s (%s) for writing, errno=%d\n",
  1615.                 temp, dstnam, errno);
  1616.         }
  1617.         logit("-> REQUEST", "FAILED -- TEMPFILE");
  1618.         return FAIL;
  1619.     }
  1620.     setvbuf(fddsk, NULL, _IOFBF, 4096);
  1621.     
  1622.     logit("-> REQUEST", buf);
  1623.     if (wrmsg('R', buf+1, fdtty) != SUCCESS) {
  1624.         printf("uucico: problem sending request\n");
  1625.         return FAIL;
  1626.     }
  1627.  
  1628.     /* See what the other side has to say about it */
  1629.     if ((long)rdmsg(buf, fdtty) != (long)SUCCESS)
  1630.         return FAIL;
  1631.     if ((buf[0] != 'R') || (buf[1] != 'Y')) {
  1632.         logit("-> REQUEST DENIED", buf);
  1633.         return SUCCESS;    /* FIXME, should do something more here */
  1634.     }
  1635.  
  1636.     return receive_file(fddsk, temp, dstnam, srcnam);
  1637.     /* FIXME - We should deal with files that didn't get there */
  1638. }
  1639.  
  1640. /* general file receive routine */
  1641. int
  1642. receive_file(fddsk, temp, dstnam, srcnam)
  1643.     FILE *fddsk;            /* Disk file pointer */
  1644.     char    *temp, *dstnam, *srcnam;
  1645. {
  1646.     int status;
  1647.     char tmp_str1[256];
  1648.     char tmp_str2[256];
  1649.     char err_str[30];
  1650.     int error = 0;            /* No errors so far */
  1651.  
  1652.     if (rddata(fdtty, fddsk) != SUCCESS) error++;
  1653.     status = fclose(fddsk);        /* Make sure the data got here */
  1654.     FlushVol(NULL, 0);
  1655.     if (status != 0) {
  1656.         error++;
  1657.         DEBUG(0, "fclose errno=%d\n", errno);
  1658.     }
  1659.  
  1660.     /* Move the file from its temp location to its real loc */
  1661.     /* FIXME:  This needs to be able to copy the file, if 
  1662.        a simple rename does not suffice. */
  1663.     /* FIXME:  should create directories if necessary, e.g. D.
  1664.        or subdirs of /usr/spool/uucppublic. */
  1665.     /* FIXME:  should use source name if target is a directory e.g. ~/ */
  1666.     sprintf(tmp_str1, "%s:%s", Spool, temp);
  1667.     sprintf(tmp_str2, "%s:%s", Spool, dstnam);
  1668.     remove(tmp_str2);
  1669.     status = rename(tmp_str1, tmp_str2);
  1670.     if (status != 0) {
  1671.         error++;
  1672.         if (DEBUG_LEVEL(0)) {
  1673.             logit("-> Cannot rename file", temp);
  1674.             logit("-> to", dstnam);
  1675.             sprintf(err_str, "%d", errno);
  1676.             logit("-> Error Code", err_str);
  1677.         }
  1678.     }
  1679.  
  1680.     logit("-> COPY", error? "FAILED": "SUCCEEDED");
  1681.     if (yesno('C', error == 0, 5))    /* Send yes or no */
  1682.         return FAIL;
  1683.     return SUCCESS;
  1684. }
  1685.  
  1686. /*
  1687.  * general file send routine
  1688.  * Return SUCCESS, FAIL, or COPYFAIL.
  1689.  */
  1690. int
  1691. send_file(fddsk)
  1692.     FILE *fddsk;     /* Disk file pointer */
  1693. {
  1694.     char ansbuf[MAXMSGLEN];
  1695.  
  1696.     if (wrdata(fddsk, fdtty) != SUCCESS)
  1697.         return FAIL;
  1698.     (void) fclose(fddsk);
  1699.     FlushVol(NULL, 0);
  1700.     /* Await the "CY" or "CNddd" packet, and toss it. */
  1701.     while (1) {
  1702.         if ((long)rdmsg(ansbuf, fdtty) != (long)SUCCESS)    
  1703.             return FAIL;
  1704.         if (ansbuf[0] != 'C') {
  1705.             DEBUG(0,"\nDidn't get 'CY' or 'CN', got %s\n",
  1706.                 ansbuf);
  1707.             /* and loop looking for C message */
  1708.         } else if (ansbuf[1] == 'Y') {
  1709.             logit("<- REQUESTED", ansbuf);
  1710.             return SUCCESS;
  1711.         } else {
  1712.             logit("<- COPY FAILED", ansbuf);
  1713.             return COPYFAIL;
  1714.         }
  1715.     }
  1716. }
  1717.  
  1718.